package com.compiler.lexico;

import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import com.compiler.lexico.action.AbstractAction;
import com.compiler.lexico.action.CompositeAction;
import com.compiler.lexico.action.Error;
import com.compiler.lexico.token.SymbolTable;
import com.compiler.lexico.token.Token;

public class Configuration {
	public final static String CONFIG_FILE_NAME = "configuration.properties";
	public final static String PACKAGE_ACTIONS = "com.compiler.lexico.action.";
	public final static String PACKAGE_ERRORS = "com.compiler.lexico.error.";
	private Map<String, AbstractAction> actions;
	private Map<String, State> states;
	private Character[] characters;
	private Token token = new Token();
	private SymbolTable symbolTable;
	private Properties properties = null;
	
	private Configuration() {
		try {
			FileInputStream in = new FileInputStream(CONFIG_FILE_NAME);
			properties = new Properties();
			properties.load(in);
			in.close();
			this.symbolTable = new SymbolTable(new Vector<Object>(Arrays.asList(properties.getProperty("reservedWords").split(","))));
			this.loadStates();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private AbstractAction getInstanceClass(String name) {
		Class<?> clase = null;
		AbstractAction action = null;
		try {
			clase = Class.forName(PACKAGE_ACTIONS+name);
			try {
				action = (AbstractAction) clase.getConstructor(new Class[] {Token.class, SymbolTable.class}).newInstance(this.token,this.symbolTable);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return action;
	}

	/*
	 * Carga las transacciones en el Map<name,class> para usar la misma instancia
	 */
	private void loadActions() {
		actions = new HashMap<String, AbstractAction>();
		boolean next = true;
		for(int x=0;next;x++) {
			String Tline = properties.getProperty("T"+x);
			if(Tline != null)
				actions.put("T"+x, getInstanceClass(Tline));
			else next = false;
		}
		this.loadErrors(actions);
	}
	
	private void loadErrors(Map<String, AbstractAction> actions) {
		boolean next = true;
		for(int x=0;next;x++) {
			String Eline = properties.getProperty("E"+x);
			if(Eline != null)
				actions.put("E"+x, new Error(this.token, Eline, this.symbolTable));
			else next = false;
		}
	}
	
	/*
	 * Retorna una accion compuesta de todas las acciones pasadas como parametro
	 */
	private AbstractAction getActions(String[] actions) {
		if(actions.length > 1) {
			CompositeAction compositeAction = new CompositeAction(this.token, this.symbolTable);
			for(String action : actions)
				compositeAction.addAction(this.actions.get(action));
			return compositeAction;
		}
		return (this.actions.get(actions[0]));
	}
	
	private void loadCharacters() {
		String[] characters = properties.getProperty("Characters").split(",");
		this.characters = new Character[characters.length];
		for(int x=0; x<characters.length; x++)
			this.characters[x] = new Character(characters[x]);
	}
	
	private Map<String, State> loadInitStates() {
		Map<String, State> states = new HashMap<String, State>();
		for(Enumeration<Object> e = properties.keys(); e.hasMoreElements();){
			String stateName = (String)e.nextElement();
			if(stateName.startsWith("e"))
				states.put(stateName, new State(stateName));
		}
		return states;
	}
	
	private void loadStates() {
		this.loadActions();
		this.loadCharacters();
		this.states = this.loadInitStates();
		boolean next = true;
		for(int x=0;next;x++) {
			String stateName = "e"+x;
			String line = properties.getProperty(stateName);
			if(line != null){
				String[] stateLine = line.split(",");
				for(int y=0; y<stateLine.length; y++) {
					String nextStateName = stateLine[y].substring(0,stateLine[y].indexOf('['));
					if(!nextStateName.startsWith("E")) {
						String[] stateActions = stateLine[y].substring(stateLine[y].indexOf('[')+1,stateLine[y].indexOf(']')).split("&");
						this.states.get(stateName).addTransacion(characters[y], new Transaction(this.states.get(nextStateName), this.getActions(stateActions)));
					}
				}
			} else next = false;
		}
	}

	public static Configuration getInstance() {
		return ConfigurationHolder.INSTANCE;
	}
	
	public static class ConfigurationHolder {
		private static final Configuration INSTANCE = new Configuration();
	}

	public Map<String, State> getStates() { return this.states; }
	public Character[] getCharacters() { return this.characters; }
	public String getError(String name) { return properties.getProperty(name); }
	public SymbolTable getSymbolTable() { return this.symbolTable; }
}
